/*
 *  Copyright (c) 2011 Leibniz Institute of Plant Genetics and Crop Plant Research (IPK), Gatersleben, Germany.
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the GNU Lesser Public License v2.1
 *  which accompanies this distribution, and is available at
 *  http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 *
 *  Contributors:
 *      Leibniz Institute of Plant Genetics and Crop Plant Research (IPK), Gatersleben, Germany - RMI Client, FileChooser and WebDAV
 */
package de.ipk_gatersleben.bit.bi.edal.webdav.wrapper.metadata;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.PropertyException;
import javax.xml.bind.Unmarshaller;
import javax.xml.validation.Schema;

import de.ipk_gatersleben.bit.bi.edal.rmi.client.ClientDataManager;

public class ExtXMLConfig {
	private JAXBContext context;
	private Marshaller m;
	private Unmarshaller um;
	private Schema schema = null;

	/**
	 * Creates an ExtXMLConfig-object, which uses rootClass as object to parse
	 * and save XML-files.
	 * 
	 * @param rootClass
	 *            the class use create/parse xml-files from
	 * @throws JAXBException
	 */
	public ExtXMLConfig(Class<?> rootClass) throws JAXBException {
		context = JAXBContext.newInstance(rootClass);
		init();
	}

	/**
	 * Creates an ExtXMLConfig, which uses a classPath like javax.xml.bin to use
	 * all classes in that path to parse and write xml-files
	 * 
	 * @param classPath
	 *            the class path containing all needed java-objects
	 * @throws JAXBException
	 */
	public ExtXMLConfig(String classPath) throws JAXBException {
		context = JAXBContext.newInstance(classPath);
		init();
	}

	/**
	 * Parses a xml-file into a JavaObject.
	 * 
	 * @param xml
	 *            File-object representing the xml-file
	 * @return a java-Object
	 */
	public Object load(File xml) {
		um.setSchema(schema);

		if (xml.exists() && xml.isFile()) {
			try {
				return um.unmarshal(xml);
			} catch (JAXBException e) {
				ClientDataManager.logger.error("Failed to load file: " + xml
						+ "\nReason: " + e.toString());
			}
		} else {
			ClientDataManager.logger.error("Failed to open file: " + xml
					+ "\nReason: " + "file doesn't exists or is not a file.");
		}

		return null;
	}

	public Object load(InputStream inputStream) {
		um.setSchema(schema);

		try {
			return um.unmarshal(inputStream);
		} catch (JAXBException e) {
			ClientDataManager.logger.error("Failed to load xml from stream: "
					+ e.toString());
		}

		return null;
	}

	public Object load(String fileInput) {
		um.setSchema(schema);

		try {
			return um.unmarshal(new StringReader(fileInput));
		} catch (JAXBException e) {
			ClientDataManager.logger.error("Failed to load xml from string: "
					+ fileInput + "\nReason: " + e.toString());
		}

		return null;
	}

	/**
	 * Saves a object into a xml-file.
	 * 
	 * @param xml
	 *            the object to save
	 * @param file
	 *            path to the file to save to
	 */
	public void save(Object xml, String file) {
		save(xml, new File(file));
	}

	/**
	 * Saves a object into a xml-file.
	 * 
	 * @param xml
	 *            the object to save
	 * @param file
	 *            File-object representing the file to save to
	 */
	public void save(Object xml, File file) {
		m.setSchema(schema);
		try {
			m.setProperty("jaxb.formatted.output", true);
		} catch (PropertyException e1) {
			// if not possible, output unformated
			ClientDataManager.logger
					.error("Failed to set formated output property: "
							+ "\nReason: " + e1.toString());

		}

		if (!file.isDirectory()) {
			try {
				if (!file.exists()) {
					file.createNewFile();
				}

				m.marshal(xml, file);
			} catch (JAXBException e) {
				ClientDataManager.logger.error("Failed to save file: " + file
						+ "\nReason: " + e.toString());
			} catch (IOException e) {
				ClientDataManager.logger.error("Failed to save file: " + file
						+ "\nReason: " + e.toString());
			}
		}
	}

	/**
	 * Returns a formatted string representation of a xml-file given as a
	 * java-Object.
	 * 
	 * @param xml
	 *            the java-object to parse the xml from.
	 * @return a formatted string representation of the given object
	 */
	public String toString(Object xml) {
		StringWriter out = new StringWriter();
		try {
			m.setSchema(schema);
			m.marshal(xml, out);

			// String ret = out.toString();
			// if(windows) {
			// ret.replaceAll("\n", "\r\n");
			// }
			//
			// return ret;

			return out.toString();
		} catch (JAXBException e) {
			ClientDataManager.logger
					.error("Failed to generate String representation for: "
							+ xml + "\nReason: " + e.toString());
		}

		return null;
	}

	// private methods

	private void init() throws JAXBException {
		m = context.createMarshaller();
		m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
		m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");

		um = context.createUnmarshaller();
	}
}
